﻿using System;
using System.Data;
using System.Diagnostics;
using System.Globalization;

namespace Salamanca.Sdml
{
    public static class TypesProvider
    {
        public static SdmlType GetType(EntityAttribute attribute)
        {
            switch (attribute.Type)
            {
            case "bool":
                return new BooleanType(attribute);
            case "DateTime":
                return new DateTimeType(attribute);
            case "float":
                return new SingleType(attribute);
            case "double":
                return new DoubleType(attribute);
            case "int":
                return new Int32Type(attribute);
            case "long":
                return new Int64Type(attribute);
            case "short":
                return new Int16Type(attribute);
            case "Guid":
                return new GuidType(attribute);
            default:
                return new StringType(attribute);
            }
        }
    }

    public abstract class SdmlType
    {
        public SdmlType(EntityAttribute attribute)
        {
            Debug.Assert(attribute!=null);
            if (attribute==null)
                throw new ArgumentNullException("attribute");

            _Attribute=attribute;
        }

        protected SdmlType()
        {
        }

        public virtual Type CSharpType
        {
            get
            {
                return DataType;
            }
        }

        public abstract Type DataType
        {
            get;
        }

        public abstract string SqlType
        {
            get;
        }

        public virtual string SqlDefault
        {
            get
            {
                return string.Empty;
            }
        }

        public abstract DbType DbType
        {
            get;
        }

        protected EntityAttribute Attribute
        {
            get
            {
                return _Attribute;
            }
        }

        private EntityAttribute _Attribute;
    }

    internal class DateTimeType:
        SdmlType
    {
        public DateTimeType(EntityAttribute attribute) :
            base(attribute)
        {
        }

        public override Type DataType
        {
            get
            {
                return typeof(DateTime);
            }
        }

        public override string SqlType
        {
            get
            {
                return "datetime";
            }
        }

        public override DbType DbType
        {
            get
            {
                return DbType.DateTime;
            }
        }
    }

    internal class BooleanType:
        SdmlType
    {
        public BooleanType(EntityAttribute attribute) :
            base(attribute)
        {
        }

        public override Type DataType
        {
            get
            {
                return typeof(bool);
            }
        }

        public override string SqlType
        {
            get
            {
                return "bit";
            }
        }

        public override DbType DbType
        {
            get
            {
                return DbType.Boolean;
            }
        }
    }

    internal class SingleType:
        SdmlType
    {
        public SingleType(EntityAttribute attribute):
            base(attribute)
        {
        }

        public override Type DataType
        {
            get
            {
                return typeof(float);
            }
        }

        public override string SqlType
        {
            get
            {
                return "real";
            }
        }

        public override DbType DbType
        {
            get
            {
                return DbType.Single;
            }
        }
    }

    internal class DoubleType:
        SdmlType
    {
        public DoubleType(EntityAttribute attribute) :
            base(attribute)
        {
        }

        public override Type DataType
        {
            get
            {
                return typeof(double);
            }
        }

        public override string SqlType
        {
            get
            {
                return "float";
            }
        }

        public override DbType DbType
        {
            get
            {
                return DbType.Double;
            }
        }
    }

    internal class StringType:
        SdmlType
    {
        public StringType(EntityAttribute attribute) :
            base(attribute)
        {
        }

        public override Type DataType
        {
            get
            {
                return typeof(string);
            }
        }

        public override string SqlType
        {
            get
            {
                return string.Format(
                    CultureInfo.InvariantCulture,
                    "varchar({0})",
                    Attribute.MaxLength>0?(object)Attribute.MaxLength:"MAX"
                );
            }
        }

        public override DbType DbType
        {
            get
            {
                return DbType.String;
            }
        }
    }

    internal class GuidType:
        SdmlType
    {
        public GuidType(EntityAttribute attribute):
            base(attribute)
        {
        }

        public GuidType()
        {
        }

        public override Type DataType
        {
            get
            {
                return typeof(Guid);
            }
        }

        public override string SqlType
        {
            get
            {
                return "uniqueidentifier";
            }
        }

        public override string SqlDefault
        {
            get
            {
                return "NEWSEQUENTIALID()";
            }
        }

        public override DbType DbType
        {
            get
            {
                return DbType.Guid;
            }
        }
    }

    internal class Int16Type:
        SdmlType
    {
        public Int16Type(EntityAttribute attribute):
            base(attribute)
        {
        }

        public Int16Type()
        {
        }

        public override Type DataType
        {
            get
            {
                return typeof(short);
            }
        }

        public override string SqlType
        {
            get
            {
                return "smallint";
            }
        }

        public override string SqlDefault
        {
            get
            {
                return "IDENTITY(1, 1)";
            }
        }

        public override DbType DbType
        {
            get
            {
                return DbType.Int16;
            }
        }
    }

    internal class Int32Type:
        SdmlType
    {
        public Int32Type(EntityAttribute attribute) :
            base(attribute)
        {
        }

        public Int32Type()
        {
        }

        public override Type DataType
        {
            get
            {
                return typeof(int);
            }
        }

        public override string SqlType
        {
            get
            {
                return "int";
            }
        }

        public override string SqlDefault
        {
            get
            {
                return "IDENTITY(1, 1)";
            }
        }

        public override DbType DbType
        {
            get
            {
                return DbType.Int32;
            }
        }
    }

    internal class Int64Type:
        SdmlType
    {
        public Int64Type(EntityAttribute attribute) :
            base(attribute)
        {
        }

        public Int64Type()
        {
        }

        public override Type DataType
        {
            get
            {
                return typeof(long);
            }
        }

        public override string SqlType
        {
            get
            {
                return "bigint";
            }
        }

        public override string SqlDefault
        {
            get
            {
                return "IDENTITY(1, 1)";
            }
        }

        public override DbType DbType
        {
            get
            {
                return DbType.Int64;
            }
        }
    }
}
